from tile import *
from ruleengine import *


class Score:
    def __init__(self, initial_points=100):
        self._points = initial_points  # 使用私有属性

    def add_points(self, points):
        if points < 0:
            raise ValueError("分数不能为负数.")
        self._points += points

    def subtract_points(self, points):
        if points < 0:
            raise ValueError("分数不能为负数.")
        # if self._points - points < 0:
        #     raise ValueError("Cannot have negative points.")
        self._points -= points

    def __str__(self):
        return str(self._points)


class Player:
    def __init__(self, name, position):
        self.name = name
        self.position = position
        self.hand = []  # 手牌
        self.side = []  # 组合复制手牌
        self.river = []  # 河牌
        self.fangchong = False  # 放铳状态
        self.win = False  # 赢家
        self.riichi_flag = False  # 听 标志
        self.riichi_list = []  # 听 哪些牌
        self.drawn_tile = None  # 刚抓的一张牌, 每个玩家都有自己的 drawn_tile 属性
        self.score = Score()  # 计分系统

    def sort_hand(self):
        # 定义种类的排序顺序 - 码将新排序
        # kind_order字典将牌的种类映射到它们的排序优先级
        kind_order = {'S': 0, 'A': 1, 'H': 2, 'T': 3, 'Q': 4}  # 对称加密、非对称加密、哈希、术语、量子
        # 先按照种类分组
        grouped_hand = {}  # 初始化一个空字典
        for tile in self.hand:  # 遍历手牌 self.hand，根据每张牌的种类将它们分组。
            kind = tile.kind
            if kind not in grouped_hand:
                grouped_hand[kind] = []  # 如果当前种类在 grouped_hand 中不存在，则创建一个新的列表。
            grouped_hand[kind].append(tile)  # 将牌添加到相应种类的列表中

        # 对每个分组内的牌进行排序
        for kind, tiles in grouped_hand.items():
            tiles.sort()  # 使用默认的排序方法，Tile类实现了__lt__方法

        sorted_hand = []  # 初始化一个空列表
        # lambda k: kind_order[k]是一个匿名函数（lambda函数），接收一个种类 k 作为输入，返回该种类在 kind_order 中的优先级。
        # sorted 函数根据 kind_order 中定义的优先级对种类进行排序
        for kind in sorted(grouped_hand.keys(), key=lambda k: kind_order.get(k, 999)):  # 使用get方法避免KeyError
            sorted_hand.extend(grouped_hand[kind])  # 按顺序将分组内的牌添加到sorted_hand中

        # 将排序后的牌重新放回手牌中
        self.hand = sorted_hand

    def sort_side(self):
        """整理手牌旁"""
        self.side.sort()

    def reset(self):
        """重置玩家状态"""
        self.hand.clear()
        self.side.clear()
        self.river.clear()
        self.riichi_list.clear()
        self.fangchong = False
        self.win = False
        self.riichi_flag = False

    def __str__(self):
        # return f"Player {self.name}: Hand: {self.hand}, Side: {self.side}, River: {self.river}"
        return f"Player {self.name}: Hand: {self.hand}, Side: {self.side}"

    def duplicate(self, current_tile):
        """复制算法"""
        # 创建一个空列表，用于存储需要移除的牌
        tiles_to_remove = []
        # 检查玩家手中是否有两张与要复制的算法相同的牌
        # 由于 check_duplicate 已经确保玩家手中有两张相同的牌，这里不再需要进一步检查
        # 遍历手牌，将两张与 current_tile 相同的牌添加到 tiles_to_remove 列表中
        for tile in self.hand:
            if tile.kind == current_tile.kind and tile.n == current_tile.n:
                # 将符合条件的牌添加到需要移除的列表中
                tiles_to_remove.append(tile)
        # 循环结束后统一移除需要移除的牌
        for tile in tiles_to_remove:
            self.hand.remove(tile)
            self.side.append(tile)
        self.side.append(current_tile)
        print(f" {self.name} 复制了 {current_tile.text}")

    def reinforce(self, tile):
        """强化算法操作"""
        # 正常"复制"强化
        if self.hand.count(tile) == 3:
            for _ in range(3):
                self.hand.remove(tile)
            self.side.extend([tile] * 4)
            print(f" {self.name} 明强化了 {tile.text}")
        # 处理暗强化（从手牌中强化）
        elif self.hand.count(tile) == 4:
            for _ in range(4):
                self.hand.remove(tile)
            self.side.extend([tile] * 4)
            print(f" {self.name} 暗强化了 {tile.text}")
        # 处理明强化（从副露中强化）
        elif self.side.count(tile) == 3 and self.hand.count(tile) == 1:
            for _ in range(3):
                self.side.remove(tile)
            self.side.extend([tile] * 4)
            self.hand.remove(tile)
            print(f" {self.name} 加强化了 {tile.text}")

    def has_consecutive_tiles(self, tile):
        """检查手牌中是否存在与给定牌连续的三张牌"""
        kind_tiles = [t for t in self.hand if t.kind == tile.kind]  # 过滤同花色的牌：首先从手牌中过滤出与tile同花色的所有牌。
        kind_numbers = [t.n for t in kind_tiles]  # 获取这些牌的数字值并排序，以便后续的顺子检查。

        # 检查当前牌及其后续两张牌是否组成顺子 tile, tile+1, tile+2 的顺子
        if tile.n in kind_numbers and tile.n + 1 in kind_numbers and tile.n + 2 in kind_numbers:
            return True
        # 检查当前牌及其前两张牌是否组成顺子 tile, tile-1, tile-2 的顺子
        if tile.n - 2 in kind_numbers and tile.n - 1 in kind_numbers and tile.n in kind_numbers:
            return True
        # 检查当前牌及其前后一张牌是否组成顺子 tile-1, tile, tile+1 的顺子
        if tile.n - 1 in kind_numbers and tile.n in kind_numbers and tile.n + 1 in kind_numbers:
            return True

        return False

    def check_combine(self, tile):
        """检查是否可以组合算法 - 码将版本"""
        # 在码将中，只有算法牌(S、A、H)可以组合，术语牌和量子牌不能组合
        if not tile.is_num or tile.kind not in ['S', 'A', 'H']:
            return False

        # 获取手牌中与打出的牌同一花色的所有牌
        certain_kind_tiles = [t for t in self.hand if t.kind == tile.kind]
        certain_kind_numbers = [t.n for t in certain_kind_tiles]

        # 检查手牌里该Tile是否已经形成算法链
        if self.has_consecutive_tiles(tile):  # 此行注释会导致组合时，如果某个player的hand手牌里3条4条5条，上家打出了4条。combine时会导致多remove掉一张4条。
            return False
        # 检查组合前面的算法
        if tile.n - 2 in certain_kind_numbers and tile.n - 1 in certain_kind_numbers:
            return True
        # 检查组合中间的算法
        if tile.n - 1 in certain_kind_numbers and tile.n + 1 in certain_kind_numbers:
            return True
        # 检查组合后面的算法
        if tile.n + 1 in certain_kind_numbers and tile.n + 2 in certain_kind_numbers:
            return True

        return False

    def combine(self, tile, combination):
        """组合算法"""
        # 将组合的算法添加到手牌旁
        self.side.extend(combination)  # extend 是将一个可迭代对象（如列表、元组等）中的所有元素添加到原列表中； append是将一个对象作为单个元素添加到列表的末尾。

        # 从手牌中移除组合的算法
        # for combine_tile in combination:
        #     if combine_tile in self.hand:
        #         self.hand.remove(combine_tile)
        # 从手牌中移除组合的算法，逐个匹配和移除
        for combine_tile in combination:
            if combine_tile != tile and combine_tile in self.hand:  # 在移除时跳过了与 tile 本身相同的牌，因为 tile 是当前被组合的牌，不需要从手牌中移除
                self.hand.remove(
                    combine_tile)  # 假设手牌里有1条2条2条3条，如果上家打出4条，那么应该移除1张2条1张3条，而不是2张2条都被移除。因为combine_tile在combination里只迭代一次。

        print(f" {self.name} 算法组合成功: {[t.n for t in combination]}")

    def check_reinforce(self, current_tile):
        # 判断是否可以强化算法的逻辑
        # 如果满足强化条件，返回 True，否则返回 False
        if self.hand.count(current_tile) == 3:
            print(f" {self.name} 可以强化 {current_tile}")
            return True
        else:
            return False

    def check_duplicate(self, current_tile):
        # 判断是否可以复制算法的逻辑
        # 如果满足复制条件，返回 True，否则返回 False

        count = 0
        for tile in self.hand:
            if tile.kind == current_tile.kind and tile.n == current_tile.n:
                count += 1
        return count == 2

    def check_riichi(self):
        # 调用之前打印手牌，确认手牌是否被篡改
        print(f"调用check_riichi之前的手牌: {self.hand}")

        converted_hand = convert_hand_to_wintile_format(self.hand)
        # print("转换后的手牌格式:", converted_hand)  # 添加调试信息，检查手牌格式
        self.riichi_list = wintile(converted_hand)
        print("分析列表:", self.riichi_list)  # 输出生成的分析列表
        if not self.riichi_list:
            return False
        return True

    def check_win(self, discard_tile):
        """检查是否可以破解"""
        # 如果打出的牌在分析列表中，返回 True，否则返回 False
        if isinstance(discard_tile, Tile):
            discard_tile_str = discard_tile.text
        else:
            discard_tile_str = discard_tile

        if discard_tile_str in self.riichi_list:
            return True
        else:
            return False

    def is_win(self, tiles):
        # 检查玩家手牌是否符合胡牌条件
        converted_hand = convert_hand_to_wintile_format(tiles)
        return checkwin(converted_hand)

    def get_combine_combinations(self, tile):
        """获取所有可能的算法组合"""
        combine_combinations = []

        # 获取手牌中与打出的牌同一花色的所有牌
        certain_kind_tiles = [t for t in self.hand if t.kind == tile.kind]
        certain_kind_numbers = sorted(set(t.n for t in certain_kind_tiles + [tile]))  # 包括tile的数字n， 去重并排序
        # 使用 len(combination) 判断是为了确保每个顺子（即每个组合）中的牌是唯一的，并且按正确的顺序添加到组合中。这样可以避免重复添加相同的牌对象
        # 添加第一张牌：当 t.n == tile.n - 1 且 combination 为空时，添加第一张牌。
        # 添加第二张牌：当 t.n == tile.n 且 combination 长度为1时，添加第二张牌（即传入的 tile）。
        # 添加第三张牌：当 t.n == tile.n + 1 且 combination 长度为2时，添加第三张牌，并终止循环。

        # 检查组合前面的算法
        if tile.n - 1 in certain_kind_numbers and tile.n - 2 in certain_kind_numbers:
            combination = []  # 初始化组合，将tile放入组合中
            seen_tiles = set()  # 记录已经添加到组合中的牌的值
            for t in self.hand:
                if t.kind == tile.kind and t.n in {tile.n - 2, tile.n - 1} and t.n not in seen_tiles:
                    combination.append(t)  # 添加手牌到组合中，这样保证Tile组合显示是从小到大
                    seen_tiles.add(t.n)  # 标记这个牌值已被添加
                    if len(combination) == 2:  # 如果组合中有3张牌，结束循环
                        break
            combination.append(tile)  # 由于for循环始终是由小到大顺序，所以后添加tile，保证顺序一致。
            if len(combination) == 3:
                combine_combinations.append(combination)  # 添加到算法组合列表

        # 检查组合中间的算法
        if tile.n - 1 in certain_kind_numbers and tile.n + 1 in certain_kind_numbers:
            combination = [tile]  # 初始化组合，将tile放入组合中
            seen_tiles = set()  # 记录已经添加到组合中的牌的值
            for t in self.hand:
                if t.kind == tile.kind and t.n in {tile.n - 1, tile.n + 1} and t.n not in seen_tiles:
                    if t.n < tile.n:
                        combination.insert(0, t)  # 如果t的值比tile小，插入组合前面
                    else:
                        combination.append(t)  # 如果t的值比tile大，插入组合后面
                    seen_tiles.add(t.n)  # 标记这个牌值已被添加
                    if len(combination) == 3:  # 如果组合中有3张牌，结束循环
                        break
            if len(combination) == 3:
                combine_combinations.append(combination)  # 添加到算法组合列表

        # 检查组合后面的算法
        if tile.n + 1 in certain_kind_numbers and tile.n + 2 in certain_kind_numbers:
            combination = [tile]  # 初始化组合，将tile放入组合中
            seen_tiles = set()  # 记录已经添加到组合中的牌的值
            for t in self.hand:
                if t.kind == tile.kind and t.n in {tile.n + 1, tile.n + 2} and t.n not in seen_tiles:
                    combination.append(t)  # 添加手牌到组合中
                    seen_tiles.add(t.n)  # 标记这个牌值已被添加
                    if len(combination) == 3:  # 如果组合中有3张牌，结束循环
                        break
            if len(combination) == 3:
                combine_combinations.append(combination)  # 添加到算法组合列表

        return combine_combinations  # 返回所有可能的算法组合


class CryptoScholar(Player):
    """密码学学者类 - 替代原来的Bot，包含历史上著名的密码学家"""
    
    # 密码学学者资料库
    SCHOLARS = {
        "图灵": {
            "full_name": "阿兰·图灵",
            "period": "1912-1954",
            "specialty": "破译密码、计算机科学",
            "famous_work": "破解恩尼格玛密码",
            "personality": "严谨而富有创造力",
            "photo_path": "密码学者/图灵.jpg",
            "knowledge": [
                "恩尼格玛密码机是二战期间德军使用的加密设备，我和团队在布莱切利园成功破解了它。",
                "密码分析的关键是找到模式。即使是最复杂的密码，也会留下一些统计学上的痕迹。",
                "计算机科学的发展离不开密码学。我设计的图灵机概念为现代计算机奠定了理论基础。",
                "频率分析是破解古典密码的有力工具，每种语言都有其字母使用频率的特征。",
                "机器能思考吗？这个问题将伴随密码学和人工智能的发展。",
                "在密码学中，随机性是安全的基石。真正的随机数生成是一个重要的研究课题。"
            ]
        },
        "香农": {
            "full_name": "克劳德·香农", 
            "period": "1916-2001",
            "specialty": "信息论、密码学理论",
            "famous_work": "《保密系统的通信理论》",
            "personality": "数学天才，喜欢探索本质",
            "photo_path": "密码学者/香农.jpg",
            "knowledge": [
                "信息论告诉我们，信息的熵决定了编码的效率。完美保密需要密钥长度至少等于明文长度。",
                "一次一密是理论上无法破解的加密方法，前提是密钥真正随机且只使用一次。",
                "混淆和扩散是现代密码学的两大原则。混淆隐藏明文与密文的关系，扩散传播变化。",
                "密码学的安全性应该完全依赖于密钥的保密，而不是算法的保密。",
                "信息的不确定性用熵来衡量。高熵的密钥提供更好的安全性。",
                "完美保密系统存在，但实际应用中我们需要在安全性和便利性之间找到平衡。"
            ]
        },
        "柯克霍夫": {
            "full_name": "奥古斯特·柯克霍夫",
            "period": "1835-1903",
            "specialty": "密码学原理、军事密码",
            "famous_work": "柯克霍夫原理",
            "personality": "原则性强，注重实用",
            "photo_path": "密码学者/柯克霍夫.jpg",
            "knowledge": [
                "柯克霍夫原理指出：密码系统的安全性不应依赖于算法的保密，而应依赖于密钥的保密。",
                "一个好的密码系统即使算法公开，也应该保持安全。这是现代密码学的基本原则。",
                "密码系统必须在实际应用中易于使用，复杂的系统往往因为使用不便而被弱化。",
                "密钥的管理和分发是密码系统中最关键的环节，比算法本身更重要。",
                "军事通信中，密码的可靠性胜过一切。宁可信息传输慢一些，也不能有安全漏洞。",
                "简单而可靠的密码方法，往往比复杂但容易出错的方法更安全。"
            ]
        }
    }
    
    def __init__(self, scholar_name, position):
        # 从学者资料库获取信息
        if scholar_name not in self.SCHOLARS:
            raise ValueError(f"未知的密码学学者: {scholar_name}")
        
        scholar_info = self.SCHOLARS[scholar_name]
        display_name = f"{scholar_info['full_name']}"
        
        super().__init__(display_name, position)
        
        self.scholar_key = scholar_name
        self.full_name = scholar_info["full_name"]
        self.period = scholar_info["period"]
        self.specialty = scholar_info["specialty"]
        self.famous_work = scholar_info["famous_work"]
        self.personality = scholar_info["personality"]
        self.photo_path = scholar_info["photo_path"]  # 添加照片路径
        self.knowledge_base = scholar_info["knowledge"]
        self.knowledge_shared = []  # 记录已分享的知识，避免重复
        self.last_share_turn = -1  # 记录上次分享知识的回合
        
    def get_scholar_introduction(self):
        """获取学者介绍"""
        return (f"我是{self.full_name}（{self.period}），"
                f"专长于{self.specialty}。"
                f"我最著名的贡献是{self.famous_work}。")
    
    def should_share_knowledge(self, turn_count, tile_played=None):
        """判断是否应该分享知识"""
        # 避免过于频繁的分享
        if turn_count - self.last_share_turn < 2:  # 减少间隔时间，从3改为2
            return False
            
        # 基于概率决定是否分享（提高到60%概率）
        import random
        if random.random() < 0.6:  # 从0.3提高到0.6
            return True
            
        # 如果打出的牌与学者专长相关，增加分享概率
        if tile_played and self.is_tile_related_to_specialty(tile_played):
            return random.random() < 0.9  # 从0.7提高到0.9
            
        return False
    
    def is_tile_related_to_specialty(self, tile):
        """判断牌是否与学者专长相关"""
        specialty_keywords = {
            "图灵": ["恩尼格玛", "计算机", "破译", "分析"],
            "香农": ["信息", "理论", "熵", "一次一密", "随机"],
            "柯克霍夫": ["密码学", "军事密码", "柯克霍夫原理"]
        }
        
        if self.scholar_key in specialty_keywords:
            keywords = specialty_keywords[self.scholar_key]
            return any(keyword in tile.text for keyword in keywords)
        
        return False
    
    def share_knowledge(self, context="general"):
        """分享密码学知识"""
        available_knowledge = [k for k in self.knowledge_base if k not in self.knowledge_shared]
        
        if not available_knowledge:
            # 如果所有知识都分享过了，重置记录
            self.knowledge_shared.clear()
            available_knowledge = self.knowledge_base
        
        import random
        knowledge = random.choice(available_knowledge)
        self.knowledge_shared.append(knowledge)
        
        # 添加个性化前缀
        prefixes = {
            "图灵": ["让我告诉你一个有趣的发现：", "在布莱切利园的时候，我们发现：", "从数学角度来看："],
            "香农": ["从信息论的角度：", "数学告诉我们：", "理论上来说："],
            "柯克霍夫": ["这让我想到：", "在密码学发展中：", "实际应用中："]
        }
        
        prefix = random.choice(prefixes.get(self.scholar_key, ["我想分享一个观点："]))
        
        return f"{prefix}{knowledge}"
    
    def make_decision(self, discard_pile, turn_count=0):
        """AI决策算法，同时可能分享知识"""
        # 原来的AI决策逻辑
        decision_tile = self._make_ai_decision(discard_pile)
        
        # 判断是否分享知识
        if self.should_share_knowledge(turn_count, decision_tile):
            self.last_share_turn = turn_count
            # 这里可以触发知识分享，实际的显示由GUI处理
            
        return decision_tile
    
    def _make_ai_decision(self, discard_pile):
        """原来的AI决策逻辑"""
        # 简单的AI策略：优先打出孤立的牌
        from collections import Counter
        
        # 统计手牌
        tile_counts = Counter(str(tile) for tile in self.hand)
        
        # 找到孤立的牌（只有一张的）
        isolated_tiles = [tile for tile in self.hand if tile_counts[str(tile)] == 1]
        
        if isolated_tiles:
            # 从孤立牌中选择数值最大的
            return max(isolated_tiles, key=lambda t: t.n if hasattr(t, 'n') else 0)
        else:
            # 如果没有孤立牌，随机选择一张
            import random
            return random.choice(self.hand)


# 保持原来的Bot类以兼容性（标记为弃用）
class Bot(Player):
    """原Bot类 - 已弃用，请使用CryptoScholar类"""
    def __init__(self, name, position):
        super().__init__(name, position)
        print(f"警告：Bot类已弃用，建议使用CryptoScholar类")
    
    def make_decision(self, discard_pile):
        """简单的AI决策算法"""
        # 原来的简单决策逻辑
        from collections import Counter
        import random
        
        tile_counts = Counter(str(tile) for tile in self.hand)
        isolated_tiles = [tile for tile in self.hand if tile_counts[str(tile)] == 1]
        
        if isolated_tiles:
            return max(isolated_tiles, key=lambda t: t.n if hasattr(t, 'n') else 0)
        else:
            return random.choice(self.hand)

